﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace UIOC.CodeAnalysis.Extensions;

internal static class AttributeDataExtensions {

    /// <summary>
    /// 优先级：1. NamedArguments匹配propertyName → 2. 构造函数参数匹配名称 → 3. 按索引取构造函数参数
    /// </summary>
    public static TypedConstant GetArgumentTypeSymbol(
        this AttributeData attributeData,
        string propertyName,
        string constructorArgumentName,
        int constructorArgumentIndex,
        SemanticModel semanticModel) {

        if (attributeData == null)
            throw new ArgumentNullException(nameof(attributeData));
        if (semanticModel == null)
            throw new ArgumentNullException(nameof(semanticModel));

        // 1. 优先匹配命名参数（NamedArguments）
        if (!string.IsNullOrEmpty(propertyName)) {
            foreach (var namedArg in attributeData.NamedArguments) {
                if (string.Equals(namedArg.Key, propertyName)) {
                    return namedArg.Value;
                }
            }
        }

        if (!string.IsNullOrEmpty(constructorArgumentName)) {
            var constructorParameterSymbolList = GetConstructorParameterSymbolList(attributeData, semanticModel);
            if (constructorParameterSymbolList != null) {
                // 查找名称匹配的构造函数参数
                for (int i = 0; i < constructorParameterSymbolList.Count; i++) {
                    if (string.Equals(constructorParameterSymbolList[i].Name, constructorArgumentName)
                        && i < attributeData.ConstructorArguments.Length) {
                        return attributeData.ConstructorArguments[i];
                    }
                }
            }
        }

        // 3. 按索引获取构造函数参数
        if (constructorArgumentIndex >= 0 && constructorArgumentIndex < attributeData.ConstructorArguments.Length) {
            return attributeData.ConstructorArguments[constructorArgumentIndex];
        }

        return default;
    }

    private static List<IParameterSymbol> GetConstructorParameterSymbolList(AttributeData attributeData, SemanticModel semanticModel) {
        try {
            // 通过语法引用获取特性语法节点
            var syntaxRef = attributeData.ApplicationSyntaxReference;
            if (syntaxRef == null)
                return null;

            if (syntaxRef.GetSyntax() is not AttributeSyntax attributeSyntax)
                return null;

            // 解析特性构造函数符号（获取参数信息）
            var attributeSymbolInfo = semanticModel.GetSymbolInfo(attributeSyntax);
            var ctorSymbol = attributeSymbolInfo.Symbol as IMethodSymbol;

            return ctorSymbol?.Parameters.ToList();
        } catch {
            // 语法解析失败时返回null
            return null;
        }
    }
}

internal static class AttributeSyntaxExtensions {

    /// <summary>
    /// 从 AttributeSyntax 获取对应的 AttributeData（特性符号）
    /// </summary>
    /// <param name="attributeSyntax">特性语法节点</param>
    /// <param name="semanticModel">语义模型</param>
    /// <returns>特性符号（AttributeData），失败返回 null</returns>
    public static (AttributeData attributeData, ISymbol declaredSymbol) GetAttributeData(this AttributeSyntax attributeSyntax, SemanticModel semanticModel) {
        if (attributeSyntax == null)
            throw new ArgumentNullException(nameof(attributeSyntax));
        if (semanticModel == null)
            throw new ArgumentNullException(nameof(semanticModel));

        // 1. 找到特性所属的声明节点（如类、方法、属性等）
        var declarationNode = FindDeclarationNode(attributeSyntax);
        if (declarationNode == null)
            return (null, null);

        // 2. 获取声明节点的符号（如类符号、方法符号）
        var declaredSymbol = GetDeclarationSymbol(declarationNode, semanticModel);
        if (declaredSymbol == null)
            return (null, null);

        // 3. 从声明符号的特性列表中匹配当前特性
        return (declaredSymbol.GetAttributes()
            .FirstOrDefault(attrData => IsMatchingAttribute(attrData, attributeSyntax)), declaredSymbol);
    }

    // 查找特性所属的声明节点
    private static SyntaxNode FindDeclarationNode(AttributeSyntax attribute) {
        // 特性通常在 AttributeList 中，向上查找包含它的声明节点
        return attribute.Ancestors()
            .FirstOrDefault(n =>
                n is MemberDeclarationSyntax ||  // 类、方法、属性等成员
                n is TypeDeclarationSyntax ||    // 类型声明
                n is ParameterSyntax ||          // 参数
                n is VariableDeclarationSyntax); // 变量
    }

    // 根据声明节点类型获取符号
    private static ISymbol GetDeclarationSymbol(SyntaxNode declarationNode, SemanticModel semanticModel) {
        return declarationNode switch {
            ClassDeclarationSyntax c => semanticModel.GetDeclaredSymbol(c),
            MethodDeclarationSyntax m => semanticModel.GetDeclaredSymbol(m),
            PropertyDeclarationSyntax p => semanticModel.GetDeclaredSymbol(p),
            FieldDeclarationSyntax f => semanticModel.GetDeclaredSymbol(f.Declaration.Variables.First()),
            ParameterSyntax p => semanticModel.GetDeclaredSymbol(p),
            VariableDeclarationSyntax v => semanticModel.GetDeclaredSymbol(v.Variables.First()),
            StructDeclarationSyntax s => semanticModel.GetDeclaredSymbol(s),
            InterfaceDeclarationSyntax i => semanticModel.GetDeclaredSymbol(i),
            EnumDeclarationSyntax e => semanticModel.GetDeclaredSymbol(e),
            _ => null
        };
    }

    // 匹配特性语法与特性符号
    private static bool IsMatchingAttribute(AttributeData attrData, AttributeSyntax attrSyntax) {
        // 匹配特性类型（考虑 [MyAttr] 与 MyAttrAttribute 的简写）
        var attrClass = attrData.AttributeClass;
        if (attrClass == null)
            return false;

        string syntaxName = attrSyntax.Name.ToString();
        string symbolName = attrClass.Name;
        if (symbolName.EndsWith("Attribute") && symbolName == $"{syntaxName}Attribute")
            syntaxName = symbolName;

        if (syntaxName != symbolName)
            return false;

        // 匹配位置（确保是同一个特性）
        var syntaxSpan = attrSyntax.GetLocation().SourceSpan;
        return attrData.ApplicationSyntaxReference?.Span == syntaxSpan;
    }
}